class Load {
  constructor() {
    this.progress = 0
    this.loadTime = 3000
    this.dom = {
      progress: $('.load .progress'),
      progressBar: $('.load .progress .bar'),
      loadText: $('.load .load-text span'),
      ce: $('.load .ce')
    }
    this.enter()
  }
  
  async enter() {
    const { progress, progressBar, loadText, ce } = this.dom
    await animate(this.loadTime, 0, 100, (val) => {
      const v = parseInt(val)
      progressBar.style.width = v + '%'
      loadText.innerText = v
      ce.style.marginLeft = progress.clientWidth * (v / 100) + 'px'
    })
    page.next()
  }
}

class P1 {
  constructor() {
    this.dom = {
      audio: $('audio'),
      musicBtn: $('.music img')
    }
    this.enter()
    this.bind()
  }

  bind() {
    const { musicBtn } = this.dom
    musicBtn.onclick = this.handleAudio.bind(this)
  }

  enter() {
    this.handleAudio()
    const { audio, musicBtn } = this.dom
    musicBtn.$show()
    audio.paused && this.play()
  }

  handleAudio() {
    const { audio } = this.dom
    if (audio.paused) {
      this.play()
    } else {
      this.pause()
    }
  }
  
  play() {
    const { audio, musicBtn } = this.dom
    audio.play()
    musicBtn.style.animation = 'rotate 3s linear infinite forwards'
  }
  
  pause() {
    const { audio, musicBtn } = this.dom
    audio.pause()
    // musicBtn.style.animation = 'none'
    musicBtn.style.removeProperty('animation')
  }
}

class P3 {
  constructor() {
    this.time = 30
    this.timer = null
    this.rederListTimer = null
    this.total = 12
    this.curr = 0
    /**
     * @type {Record<string, HTMLElement>}
    */
    this.dom = {
      p3: $('.p3'),
      count: $('.p3 .count'),
      mask: $('.p3 .mask'),
      success: $('.p3 .success'),
      fail: $('.p3 .fail'),
      again: $('.p3 .again'),
      currEle: $('.p3 .current'),
      totalEle: $('.p3 .total'),
      items: $all('.p3 .list li'),
      flight: $('.p3 .flight'),
      goHome: $('.p3 .go-home'),
      headerRabbit: $('.p3 .header-rabbit')
    }
    this.option = {
      rabbit: {
        img: './img/p3/rabbit.png',
        tag: 'rabbit'
      },
      frog: {
        img: './img/p3/frog.png',
        tag: 'frog'
      }
    }
    this.render()
    this.bind()
    this.enter()
  }

  bind() {
    const { again, items, flight, goHome } = this.dom
    again.onclick = this.again.bind(this)
    flight.onclick = this.flight.bind(this)
    goHome.onclick = this.goHome.bind(this)
    items.forEach(item => {
      item.onclick = this.handleSelect.bind(this, item)
    })
  }

  handleSelect(item) {
    const { headerRabbit, p3 } = this.dom
    if (!item.tag) return
    if (item.tag === 'frog') this.fail()
    if (item.tag === 'rabbit') {
      const itemImg = item.children[0]
      const w = getComputedStyle(itemImg).width
      const endW = getComputedStyle(headerRabbit).width
      const { top, left } = itemImg.getBoundingClientRect()
      const { top: endTop, left: endLeft } = headerRabbit.getBoundingClientRect()
      const img = itemImg.cloneNode(true)
      img.style.width = w
      img.style.position = 'absolute'
      img.style.top = top + 'px'
      img.style.left = left + 'px'
      img.style.transition = 'all 1s'
      p3.appendChild(img)
      requestAnimationFrame(() => {
        img.style.width = endW
        img.style.top = endTop + 'px'
        img.style.left = endLeft + 'px'
        img.addEventListener('transitionend', () => void img.remove())
      })

      item.tag = ''
      this.add()
    }
  }
  
  enter() {
    const { count } = this.dom
    this.countDown(
      (time) => {
        count.innerText = time + 's'
      },
      this.fail
    )
    this.curr = 0
    this.render()
    this.renderList()
  }

  render() {
    const { total, curr, dom: { currEle, totalEle } } = this
    currEle.innerText = curr
    totalEle.innerText = total
  }

  renderList() {
    const { dom: { items }, option } = this
    _render.apply(this)
    function _render() {
      {
        const randomIndex = this.random(items.length)
        items.forEach((item, i) => {
          item.children[0].src = ''
          item.tag = ''
        })
        const currImg = items[randomIndex].children[0]
        const rf = this.ribbitOrFrog
        currImg.src = option[rf].img
        items[randomIndex].tag = option[rf].tag
      }
    }
    this.rederListTimer = setInterval(_render.bind(this), 1000)
  }

  random(length) {
    return Math.random() * length | 0
  }

  get ribbitOrFrog() {
    return Math.random() * 10 < 8 ? 'rabbit' : 'frog'
  }

  countDown(onProgress, onFinish) {
    let time = this.time
    onProgress(time)
    this.timer = setInterval(() => {
      time--
      onProgress(time)
      if (time <= 0) {
        this.timer && clearInterval(this.timer)
        onFinish && onFinish.apply(this)
      }
    }, 1000)
  }

  add() {
    this.curr++
    this.render()
    if (this.curr >= this.total) {
      this.success()
    }
  }

  fail() {
    if (this.rederListTimer) {
      clearInterval(this.rederListTimer)
    }
    if (this.timer) {
      clearInterval(this.timer)
    }
    const { mask, fail, success } = this.dom
    mask.$show()
    fail.$show()
    success.$hide()
    animate(500, 0, 1, (val) => {
      fail.querySelector('.status').style.transform = `scale(${val})`
    })
  }

  success() {
    if (this.rederListTimer) {
      clearInterval(this.rederListTimer)
    }
    if (this.timer) {
      clearInterval(this.timer)
    }
    const { mask, success, fail } = this.dom
    mask.$show()
    success.$show()
    fail.$hide()
    animate(500, 0, 1, (val) => {
      success.querySelector('.status').style.transform = `scale(${val})`
    })
  }
  
  flight() {
    this.reset()
    page.next()
  }

  goHome() {
    this.reset()
    page.goHome()
  }

  again() {
    this.reset()
    this.enter()
  }

  reset() {
    const { mask, fail } = this.dom
    mask.$hide()
    fail.$hide()
  }
}

class P4 {
  constructor() {
    this.dom = {
      ce: $('.p4 .ce'),
      flight: $('.p4 .right-away'),
      petal: $('.p4 .petal')
    }
    this.bind()
    this.enter()
  }
  
  bind() {
    const { flight } = this.dom
    flight.onclick = this.flight.bind(this)
  }
  
  enter() {
    const { petal } = this.dom
    let w = 6
    for (let i = 0; i < 5; i++) {
      const img = document.createElement('img')
      img.src = './img/p4/petal.png'
      img.style.width = w + 'vw'
      w--
      img.style.transform = `rotate(${45 * w}deg)`
      img.style.left = this.random(0, 100) - (w / 2) + 'vw'
      animate(this.random() * 100, 0, 100, (val) => {
        img.style.top = val + 'vh'
      }, () => {
        img.remove()
      })
      petal.append(img)
    }
  }
  
  random(min = 20, max = 30) {
    return (Math.random() * (max - min) | 0) + min
  }

  async flight() {
    const { ce } = this.dom
    const w = ce.clientWidth

    await animate(1000, 16, w / 2, (val) => {
      ce.style.left = parseInt(val) + 'vw'
    })
    ce.style.transform = 'rotateY(180deg)'

    await animate(1000, w / 2, 16, (val) => {
      ce.style.left = parseInt(val) + 'vw'
    })
    ce.style.transform = 'rotateY(0deg)'

    animate(2000, w, w / 2, val => {
      ce.style.width = val + 'px'
    })
    
    animate(1000, 16, 55, (val) => {
      ce.style.left = parseInt(val) + 'vw'
    })
    await animate(1000, 33, 72, (val) => {
      ce.style.bottom = parseInt(val) + 'vw'
    })

    ce.style.transform = 'rotateY(180deg)'

    await animate(1000, 67, 130, (val) => {
      ce.style.bottom = parseInt(val) + 'vw'
    })

    page.next()
    setTimeout(() => {
      ce.style.width = w + 'px'
      ce.style.transform = 'rotateY(0deg)'
      ce.style.left = 16 + 'vw'
      ce.style.bottom = 30 + 'vw'
    }, 500)
  }
}

class P5 {
  constructor() {
    /**
     * @type {Record<string, Element>}
     */
    this.dom = {
      text: $('.p5 .text')
    }
    this.writingText = [
      '亲爱的你',
      '又是一年中秋佳节，再次我们祝福您。中秋',
      '快乐，阖家团圆。',
      'END',
    ]
    this.enter()
  }

  async enter() {
    const { text } = this.dom
    const { writingText } = this
    await this.typewriting(text.children[0], writingText[0])
    await this.typewriting(text.children[1], writingText[1])
    await this.typewriting(text.children[2], writingText[2])
    await this.typewriting(text.children[3].children[0], writingText[3])
  }

  /**
   * 
   * @param {HTMLElement} dom
   * @param {string} text
   * @param {Function} onProgress
   * @param {Function} onFinish
   * @returns {Promise<void>}
   */
  typewriting(dom, text) {
    return new Promise(resolve => {
      const len = text.length
      let i = 0
      this.timer = setInterval(() => {
        dom.innerText = text.slice(0, i)
        if (i >= len) {
          clearInterval(this.timer)
          resolve()
        }
        i++
      }, 100)
    })
  }
}

class Page {
  constructor() {
    this.oldPage = -1
    this.page = 0
    /**
     * @type {Record<string, HTMLElement[]>}
     */
    this.dom = {
      pages: $all('.page')
    }
    this.renderOrder()
    new Load()
  }

  renderOrder() {
    const { pages } = this.dom
    pages.forEach((item, i) => {
      item.style.zIndex = pages.length - i
      if (i !== 0) item.$hide()
    })
  }

  async changePage() {
    const { oldPage, page, dom: { pages } } = this
    pages[oldPage].$fadeOut()
    await pages[page].$fadeIn()
    this.checkPage()
  }

  checkPage() {
    const { page } = this
    if (page === 1) new P1()
    if (page === 3) new P3()
    if (page === 4) new P4()
    if (page === 5) new P5()
  }

  next() {
    this.oldPage = this.page
    this.page++
    this.changePage()
  }

  goHome() {
    this.oldPage = this.page
    this.page = 1
    this.changePage()
  }
}

const page = new Page()
